package com.yjk.dzpoke.websocket;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.yjk.dzpoke.websocket.bean.*;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.yjk.dzpoke.websocket.bean.WsRequest.*;


@Slf4j
@ServerEndpoint("/ws/play")
@Component
@Data
public class PlayServer {
    /**
     * 连接管理
     * sessionId - 连接
     */
    private static ConcurrentHashMap<String, PlayServer> webSocketMap = new ConcurrentHashMap<>();

    public static ConcurrentHashMap<String, PlayServer> getWebSocketMap() {
        return webSocketMap;
    }

    /**
     * 游戏桌
     * gameId - game
     */
    private static ConcurrentHashMap<String, Game> gameMap = new ConcurrentHashMap<>();

    /**
     * 玩家在哪个游戏中 -1在大厅
     * sessionId - gameId
     */
    private static ConcurrentHashMap<String, String> playerGameMap = new ConcurrentHashMap<>();

    private static synchronized Integer getSequence() {
        Integer seq = 0;
        for (Map.Entry<String, PlayServer> entry : webSocketMap.entrySet()) {
            PlayServer server = entry.getValue();
            Integer sequence = server.sequence;
            if (sequence != null && sequence > seq)
                seq = sequence;
        }
        return seq + 1;
    }

    /**
     * 连接
     */
    private Session session;

    public String getSessionId() {
        return session.getId();
    }

    /**
     * 昵称
     */
    private String playerName;

    /**
     * 顺序
     */
    private Integer sequence;

    private Integer sequence() {
        return sequence;
    }

    /**
     * 准备
     */
    private boolean ready = false;

    public String getPlayerName() {
        return playerName;
    }


    public void sendMessage(String message) throws IOException {
        synchronized (session) {
            session.getBasicRemote().sendText(message);
        }
    }

    public void sendMessageByUid(String uid, String message) throws IOException {
        PlayServer playServer = webSocketMap.get(uid);
        Session session = playServer.getSession();
        synchronized (session) {
            session.getBasicRemote().sendText(message);
        }
    }

    public static void sendMessageByIds(List<String> playerIds, String message) {
        for (String player : playerIds) {
            PlayServer webSocketServer = webSocketMap.get(player);
            new Thread(() -> {
                try {
                    webSocketServer.sendMessage(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    public static void sendMessageToPlayers(List<PlayServer> players, String message) {
        List<String> playerIds = players.stream().map(PlayServer::getSessionId).collect(Collectors.toList());
        sendMessageByIds(playerIds, message);
    }

    public static void sendMessage(String playerId, String message) {
        PlayServer webSocketServer = webSocketMap.get(playerId);
        new Thread(() -> {
            try {
                webSocketServer.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void sendMessageToGameHall(final String message) {
        for (Map.Entry<String, PlayServer> entry : webSocketMap.entrySet()) {
            String playerId = entry.getKey();
            String playerStatus = playerGameMap.get(playerId);
            if ("-1".equals(playerStatus)) {
                final PlayServer webSocketServer = entry.getValue();
                new Thread(() -> {
                    try {
                        webSocketServer.sendMessage(message);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }

    public static void sendMessageToGame(String gameId, final String message) {
        for (Map.Entry<String, PlayServer> entry : webSocketMap.entrySet()) {
            String playerId = entry.getKey();
            String game = playerGameMap.get(playerId);
            if (gameId.equals(game)) {
                final PlayServer webSocketServer = entry.getValue();
                new Thread(() -> {
                    try {
                        webSocketServer.sendMessage(message);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }

    public static void sendMessageAll(final String message) {
        Set<Map.Entry<String, PlayServer>> entries = webSocketMap.entrySet();
        for (Map.Entry<String, PlayServer> entry : entries) {
            final PlayServer webSocketServer = entry.getValue();
            new Thread(() -> {
                try {
                    webSocketServer.sendMessage(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        Map<String, List<String>> map = session.getRequestParameterMap();
        String name = map.get("name").get(0);
        this.playerName = name;
        log.info("id:{},昵称：{} 已经连接", getSessionId(), playerName);
        this.sequence = getSequence();
        webSocketMap.put(getSessionId(), this);
        playerGameMap.put(getSessionId(), "-1");
        sendMessageToGameHall(JSONUtil.parse(Arrays.asList(
                WsMsg.chatMsg("系统", "欢迎玩家" + name + "进入房间"),
                WsMsg.playersRoom(showRoomPlayers())
        )).toString());
    }


    //关闭连接时调用
    @OnClose
    public void onClose() {
        log.info("id:{},昵称：{} 已经退出", getSessionId(), playerName);
        webSocketMap.remove(getSessionId());
        sendMessageAll(JSONUtil.parse(Arrays.asList(
                WsMsg.chatMsg("系统", "玩家" + playerName + "离开房间"),
                WsMsg.playersRoom(showRoomPlayers())
        )).toString());
    }

    //收到客户端信息
    @OnMessage
    public void onMessage(String message) throws IOException, InterruptedException {
        System.out.println("msg:" + message);
        WsRequest request = JSONUtil.toBean(message, WsRequest.class);
        Integer type = request.getType();
        Object params = request.getParams();
        switch (type) {
            case WsRequest.ready:
                ready();
                break;
            case startGame:
                start();
                break;
            case chat:
                chat(params);
                break;
            case deal:
                deal(params);
                break;
            default:
                break;
        }
    }

    private void deal(Object params) {
        Integer choice = (Integer) params;
        String gameId = playerGameMap.get(getSessionId());
        if (gameId == null || "-1".equals(gameId)) {
            System.out.println("玩家在大厅");
            return;
        }
        Game game = gameMap.get(gameId);
        if (game == null) {
            System.out.println("找不到游戏");
            return;
        }
        GameTurn gameTurn = game.getGameTurn();
        if (!getSessionId().equals(gameTurn.getCurrentPlayer())) {
            System.out.println("不是操作者");
            return;
        }
        System.out.println(playerName + "选择" + params);
        gameTurn.input(choice);
    }

    private void chat(Object params) {
        String gameId = playerGameMap.get(getSessionId());
        if (gameId == null) return;
        String msg = JSONUtil.parse(Arrays.asList(
                WsMsg.chatMsg(playerName, (String) params)
        )).toString();
        if ("-1".equals(gameId)) {
            sendMessageToGameHall(msg);
        } else {
            Game game = gameMap.get(gameId);
            if (game == null) return;
            sendMessageByIds(game.getPlayers(), msg);
        }
    }

    private void ready() {
        this.ready = true;
        sendMessageToGameHall(JSONUtil.parse(Arrays.asList(
                WsMsg.chatMsg("系统", "玩家" + this.playerName + "已准备"),
                WsMsg.playersRoom(showRoomPlayers())
        )).toString());
    }

    private void start() throws InterruptedException {
        List<PlayServer> players = gameHallPlayers();
        PlayServer startPlayer = getStartPlayer(players);
        if (!startPlayer.getSessionId().equals(this.getSessionId())) {
            sendMessage(this.getSessionId(), JSONUtil.parse(Arrays.asList(
                    WsMsg.systemMsg("需要房主开始游戏")
            )).toString());
            return;
        }

        List<PlayServer> readyPlayers = getReadyPlayers(players);
        if (readyPlayers.size() < 2) {
            sendMessage(this.getSessionId(), JSONUtil.parse(Arrays.asList(
                    WsMsg.systemMsg("需要至少两人准备")
            )).toString());
            return;
        }

        readyPlayers.add(this);

        Game game = new Game();
        String gameId = IdUtil.simpleUUID();
        gameMap.put(gameId, game);
        for (PlayServer readyPlayer : readyPlayers) {
            playerGameMap.put(readyPlayer.getSessionId(), gameId);
        }

        sendMessageToPlayers(readyPlayers, JSONUtil.parse(Arrays.asList(
                WsMsg.gameStart()
        )).toString());

        game.init(gameId, readyPlayers, 100);
//        game.start();
        new Thread(() -> {
            try {
                game.start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("游戏开始");
    }

    private List<PlayServer> gameHallPlayers() {
        List<PlayServer> list = new ArrayList<>();
        for (Map.Entry<String, PlayServer> entry : webSocketMap.entrySet()) {
            PlayServer player = entry.getValue();
            if ("-1".equals(playerGameMap.get(player.getSessionId())))
                list.add(player);
        }
        list.sort(Comparator.comparingInt(PlayServer::sequence));
        return list;
    }

    private PlayServer getStartPlayer() {
        List<PlayServer> list = gameHallPlayers();
        PlayServer playServer = list.stream().min(Comparator.comparingInt(PlayServer::sequence)).get();
        return playServer;
    }

    private PlayServer getStartPlayer(List<PlayServer> list) {
        PlayServer playServer = list.stream().min(Comparator.comparingInt(PlayServer::sequence)).get();
        return playServer;
    }

    private List<PlayServer> getReadyPlayers(List<PlayServer> list) {
        return list.stream().filter(i -> i.ready).collect(Collectors.toList());
    }

    private String showRoomPlayers() {
        String msg = "大厅玩家：";
        List<PlayServer> list = gameHallPlayers();
        for (PlayServer player : list) {
            msg += player.playerName;
            if (list.indexOf(player) == 0) {
                msg += "(房主) ";
            } else {
                if (player.ready) {
                    msg += "(准备) ";
                } else {
                    msg += " ";
                }
            }
        }
        return msg;
    }

    private void adminMode(String msg) throws IOException, InterruptedException {
        String cmd = msg.substring(5);
        if ("end".equals(cmd)) {
            return;
        }
    }

    //错误时调用
    @OnError
    public void onError(Session session, Throwable throwable) throws IOException {
        log.info("出现错误了");
        log.info("id:{},昵称：{} 出错", getSessionId(), playerName);
        throwable.printStackTrace();
        webSocketMap.remove(getSessionId());
        session.close();
    }
}